﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization.Formatters.Binary;

using NHibernate;

using ProjectBase.Core;

namespace ProjectBase.Data
{
    public static partial class DaoExtensions
    {
        #region SetId
        /// <summary>
        ///     Provides quick way on DataLayer to use the 'ISetID' interface - to set the ID.
        ///     The ID should not be changed in upper layers (this is the DATA storage feature)
        ///     Therefore the Explicit interface implementation was used,
        ///     and only on Data layer is this Extension available
        /// </summary>
        /// <param name = "entity"></param>
        /// <param name = "id"></param>
        /// <returns>this entity</returns>
        public static TSource SetId<TSource, TID>(this TSource entity, TID id)
            where TSource : ISetID<TID>, IPersistentObjectWithTypedId<TID>
        {
            entity.SetID(id);
            return entity;
        }
        #endregion SetId

        #region SaveOrUpdateImported list
        /// <summary>
        ///     Stores all entities in the list within one transaction (if none exists)
        ///     or simply calls SaveOrUpdate() (if transaction already exists)
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "session"></param>
        /// <param name = "entities"></param>
        /// <returns></returns>
        public static IList<T> SaveOrUpdateImported<T>(this ISession session, IList<T> entities)
            where T : class, IPersistentObject, new()
        {
            if (session.Transaction.IsActive) // useful for Tests, to be rolled-back
            {
                foreach (var entity in entities)
                {
                    session.SaveOrUpdate(entity); // store provided entity in the DB storage
                }
                session.Flush(); // crucial to not keep all changes in memory.
            }
            else // Transaction is called for this IList<entities>
            {
                try
                {
                    session.Transaction.Begin();
                    foreach (var entity in entities)
                    {
                        session.SaveOrUpdate(entity); // store provided entity in the DB storage
                    }
                    session.Transaction.Commit();
                }
                catch
                {
                    session.Transaction.Rollback();
                    throw;
                }
            }
            session.Clear(); // crucial cleaning for performance improvement
            return entities;
        }
        #endregion SaveOrUpdateImported list

        #region Subcriteria
        /// <summary>
        ///     NHibernate 2.0.1 is not working properly with null Aliases.
        ///     They cannot be changed (IDictionary.Remove(null) is not working)
        ///     when calling SubCriteria.Alias set().
        ///     This extension sets the Alias based on the Association path with '_' instead of '.'
        /// </summary>
        /// <param name = "criteria"></param>
        /// <param name = "associationPath"></param>
        /// <returns></returns>
        public static ICriteria CreateSubCriteria(this ICriteria criteria, string associationPath)
        {
            var result = criteria.GetCriteriaByPath(associationPath);
            if (result.IsNull())
            {
                result = criteria.CreateCriteria(associationPath, associationPath.Replace('.', '_'));
            }
            return result;
        }

        /// <summary>
        ///     NHibernate 2.0.1 is not working properly with null Aliases.
        ///     They cannot be changed (IDictionary.Remove(null) is not working)
        ///     when calling SubCriteria.Alias set().
        ///     This extension sets the Alias based on the Association path with '_' instead of '.'
        /// </summary>
        /// <param name = "criteria"></param>
        /// <param name = "expr"></param>
        /// <returns></returns>
        public static ICriteria CreateSubCriteria<TResult>(this ICriteria criteria, Expression<Func<TResult>> expr)
        {
            //Contract.Requires(expr.Is(), " The expression parameter (Expression<Func<TResult>>) cannot be null");
            MemberExpression body;
            switch (expr.Body.NodeType)
            {
                case ExpressionType.MemberAccess:
                    body = expr.Body as MemberExpression;
                    break;

                default:
                    throw new NotSupportedException(
                        "Provided 'Expression<Func<string>> expr' parameter must be of a 'ExpressionType.MemberAccess'. ");
            }
            return CreateSubCriteria(criteria, body.Member.Name);
        }
        #endregion Subcriteria

        #region Clone
        static readonly BinaryFormatter BinaryFormatter = new BinaryFormatter();

        public static T Clone<T, TID>(this T entity)
            where T : class, IPersistentObjectWithTypedId<TID>
        {
            if (entity.IsNull())
            {
                return null;
            }
            using (var msObj = new MemoryStream())
            {
                BinaryFormatter.Serialize(msObj, entity);
                msObj.Position = 0;
                var cloned = BinaryFormatter.Deserialize(msObj) as T;
                return cloned;
            }
        }
        #endregion Clone
    }
}